﻿
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows.Threading;

namespace Red.Core.Containers
{
    /// <summary>
    ///     Extended ObservableCollection
    /// </summary>
    public class ObservableCollectionExt<T> : ObservableCollection<T>, IRangedCollection<T>
    {
        /// <summary>
        ///     Removes all the items that satisfy the given predicate
        /// </summary>
        public int RemoveAll( Predicate<T> predicate )
        {
            CheckReentrancy();

            int removedCount = 0;
            for ( int i = Items.Count - 1; i >= 0; i-- )
                if ( predicate( Items[i] ) )
                {
                    Items.RemoveAt( i );
                    ++removedCount;
                }

            if ( removedCount != 0 )
            {
                OnPropertyChanged( new PropertyChangedEventArgs( "Count" ) );
                OnPropertyChanged( new PropertyChangedEventArgs( "Item[]" ) );
                OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Reset ) );
            }

            return removedCount;
        }

        /// <summary>
        ///     Replaces the content with given elements
        /// </summary>
        public void ResetWith( IEnumerable<T> elements )
        {
            CheckReentrancy();

            Items.Clear();
            foreach ( var elem in elements )
                Items.Add( elem );

            OnPropertyChanged( new PropertyChangedEventArgs( "Count" ) );
            OnPropertyChanged( new PropertyChangedEventArgs( "Item[]" ) );
            OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Reset ) );
        }

        /// <summary>
        ///     Adds elements form a given range
        /// </summary>
        public void AddRange( IEnumerable<T> elements )
        {
            CheckReentrancy();

            foreach ( var elem in elements )
                Items.Add( elem );

            OnPropertyChanged( new PropertyChangedEventArgs( "Count" ) );
            OnPropertyChanged( new PropertyChangedEventArgs( "Item[]" ) );
            OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Reset ) );
        }

        #region Events

        /// <summary>
        ///     Thread-safe version ( solves the problem of concurrent operations on ObservableCollection like modification and coupling with CollectionView )
        /// </summary>
        public override event NotifyCollectionChangedEventHandler CollectionChanged;

        protected override void OnCollectionChanged( NotifyCollectionChangedEventArgs e )
        {
            if ( CollectionChanged != null )
                foreach ( NotifyCollectionChangedEventHandler notificationHandler in CollectionChanged.GetInvocationList() )
                {
                    DispatcherObject dispatcherObject = notificationHandler.Target as DispatcherObject;
                    if ( dispatcherObject != null )
                    {
                        Dispatcher dispatcher = dispatcherObject.Dispatcher;
                        if ( dispatcher != null && !dispatcher.CheckAccess() )
                        {
                            dispatcher.BeginInvoke(
                                (Action)( () => notificationHandler.Invoke( this,
                                     new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Reset ) ) ),
                                DispatcherPriority.DataBind );
                            continue;
                        }
                    }
                    notificationHandler.Invoke( this, e );
                }
        }

        #endregion Events
    }

}